package com.gamejelly.proxy.client;

import io.netty.util.concurrent.FastThreadLocal;

import io.protostuff.LinkedBuffer;
import lombok.experimental.UtilityClass;
import org.springframework.util.Assert;

import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Created by haoyifen on 2017/6/17 23:44.
 */
@UtilityClass
public class ProxyUtil {

    private final Map<String, Object> proxyMap = new ConcurrentHashMap<>();

    public <T> T serviceProxy(Class<T> serviceInterface, String... address) {
        return serviceProxy("", serviceInterface, address);
    }

    public <T> T serviceProxy(String strategy, Class<T> serviceInterface, String... address) {
        Assert.notEmpty(address, serviceInterface + " : address is empty");
        String sign;
        if (address.length == 1)
            sign = sign(address[0], serviceInterface);
        else
            sign = sign("default", serviceInterface);

        Object proxy = proxyMap.get(sign);
        if (proxy == null) {
            proxy = createProxy(strategy, serviceInterface, address);
            proxyMap.put(sign, proxy);
        }

        return (T) proxy;
    }


    private synchronized Object createProxy(String strategy, Class<?> itf, String... address) {
        LoadBalance loadBalance = LoadBalance.build(strategy, address);

        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{itf},
                new ProxyHandle(itf, loadBalance));
    }

    private String sign(String address, Class<?> itf) {
        return address + ":" + itf.getName();
    }

    private final FastThreadLocal<Map<String, Object>> Context = new FastThreadLocal<>();

    public void setAddress(String address) {
        Map<String, Object> map = Context.get();
        if (map == null) {
            map = new HashMap<>(4);
            Context.set(map);
        }
        map.put("rpcAddress", address);
    }

    public String cancelAddress() {
        Map<String, Object> map = Context.get();
        if (map == null) {
            map = new HashMap<>(4);
            Context.set(map);
        }
        return (String) map.remove("rpcAddress");
    }

    public LinkedBuffer getBuffer() {
        Map<String, Object> map = Context.get();
        if (map == null) {
            map = new HashMap<>(4);
            Context.set(map);
        }
        return (LinkedBuffer) map.computeIfAbsent("buffer", k -> LinkedBuffer.allocate(2048));
    }

}
